#pragma once

#include <cassert>

CFT_DECLARE_CLASS_ENUM(DBCFieldFormat_e, unsigned int,
    DBC_FF_NA = 'x',                                        // ignore/ default, 4 byte size, in Source String means field is ignored, in Dest String means field is filled with default value
    DBC_FF_NA_BYTE = 'X',                                   // ignore/ default, 1 byte size, see above
    DBC_FF_NA_FLOAT = 'F',                                  // ignore/ default,  float size, see above
    DBC_FF_NA_POINTER = 'p',                                // fill default value into dest, pointer size, Use this only with static data (otherwise mem-leak)
    DBC_FF_STRING = 's',                                    // char*
    DBC_FF_FLOAT = 'f',                                     // float
    DBC_FF_INT = 'i',                                       // unsigned int
    DBC_FF_BYTE = 'b',                                      // unsigned char
    DBC_FF_SORT = 'd',                                      // sorted by this field, field is not included
    DBC_FF_IND = 'n',                                       // the same,but parsed to data
    DBC_FF_LOGIC = 'l'                                          // Logical (boolean)
);

namespace dbc
{

    class Loader
    {
    public:
        Loader();
        ~Loader();
        bool Load(const char* filename, const char* fmt);

        class Record
        {
        public:
            float getFloat(size_t field) const
            {
                assert(field < file.field_count_);
                float val = *reinterpret_cast<float*>(offset + file.GetOffset(field));
                //EndianConvert(val);
                return val;
            }
            unsigned int getUInt(size_t field) const
            {
                assert(field < file.field_count_);
                unsigned int val = *reinterpret_cast<unsigned int*>(offset + file.GetOffset(field));
                //EndianConvert(val);
                return val;
            }
            unsigned char getUInt8(size_t field) const
            {
                assert(field < file.field_count_);
                return *reinterpret_cast<unsigned char*>(offset + file.GetOffset(field));
            }

            const char* getString(size_t field) const
            {
                assert(field < file.field_count_);
                size_t stringOffset = getUInt(field);
                assert(stringOffset < file.string_size_);
                return reinterpret_cast<char*>(file.string_table_ + stringOffset);
            }

        private:
            Record(Loader& file_, unsigned char* offset_) : offset(offset_), file(file_) {}
            unsigned char* offset = nullptr;
            Loader& file;

            friend class Loader;
        };
        std::shared_ptr<Record> GetRecord(std::size_t id);
        unsigned int GetNumRows() const { return record_count_; }
        unsigned int GetCols() const { return field_count_; }
        unsigned int GetOffset(size_t id) const { return (fields_offset_ != nullptr && id < field_count_) ? fields_offset_[id] : 0; }
        bool IsLoaded() const { return (data_ != nullptr); }
        char* AutoProduceData(const char* fmt, unsigned int& count, char**& indexTable);
        char* AutoProduceStrings(const char* fmt, char* dataTable);
        static unsigned int GetFormatRecordSize(const char* format, int* index_pos = nullptr);
        const std::string& GetFmt() const { return fmt_; }

    private:
        std::string fmt_ = "";
        unsigned int record_size_ = 0;
        unsigned int record_count_ = 0;
        unsigned int field_count_ = 0;
        unsigned int string_size_ = 0;
        unsigned int* fields_offset_ = 0;
        unsigned char* data_ = nullptr;
        unsigned char* string_table_ = nullptr;
    };

}

